Use strong_parameters and drop protected_attributes

* Ensure admin attribute can not be set at sign up and the agents user_id is not changeable
* Remove ACCESSIBLE_ATTRIBUTES from the User model
* Remove side effect on agent_params from AgentsController#build_agent

Dominik Sander 7 years ago
parent
commit
b2f031003f

+ 0 - 1
Gemfile

@@ -80,7 +80,6 @@ unless Gem::Version.new(Bundler::VERSION) >= Gem::Version.new('1.5.0')
80 80
   exit 1
81 81
 end
82 82
 
83
-gem 'protected_attributes', '~>1.0.8' # This must be loaded before some other gems, like delayed_job.
84 83
 gem 'ace-rails-ap', '~> 2.0.1'
85 84
 gem 'bootstrap-kaminari-views', '~> 0.0.3'
86 85
 gem 'bundler', '>= 1.5.0'

+ 0 - 3
Gemfile.lock

@@ -387,8 +387,6 @@ GEM
387 387
       multi_json (~> 1.0)
388 388
       websocket-driver (>= 0.2.0)
389 389
     polyglot (0.3.5)
390
-    protected_attributes (1.0.8)
391
-      activemodel (>= 4.0.1, < 5.0)
392 390
     pry (0.10.3)
393 391
       coderay (~> 1.1.0)
394 392
       method_source (~> 0.8.1)
@@ -656,7 +654,6 @@ DEPENDENCIES
656 654
   omniauth-wunderlist!
657 655
   pg (~> 0.18.3)
658 656
   poltergeist
659
-  protected_attributes (~> 1.0.8)
660 657
   pry-byebug
661 658
   pry-rails
662 659
   quiet_assets

+ 0 - 1
app/concerns/oauthable.rb

@@ -3,7 +3,6 @@ module Oauthable
3 3
 
4 4
   included do |base|
5 5
     @valid_oauth_providers = :all
6
-    attr_accessible :service_id
7 6
     validates_presence_of :service_id
8 7
   end
9 8
 

+ 6 - 6
app/controllers/admin/users_controller.rb

@@ -19,10 +19,8 @@ class Admin::UsersController < ApplicationController
19 19
   end
20 20
 
21 21
   def create
22
-    admin = params[:user].delete(:admin)
23
-    @user = User.new(params[:user])
22
+    @user = User.new(user_params)
24 23
     @user.requires_no_invitation_code!
25
-    @user.admin = admin
26 24
 
27 25
     respond_to do |format|
28 26
       if @user.save
@@ -40,10 +38,8 @@ class Admin::UsersController < ApplicationController
40 38
   end
41 39
 
42 40
   def update
43
-    admin = params[:user].delete(:admin)
44 41
     params[:user].except!(:password, :password_confirmation) if params[:user][:password].blank?
45
-    @user.assign_attributes(params[:user])
46
-    @user.admin = admin
42
+    @user.assign_attributes(user_params)
47 43
 
48 44
     respond_to do |format|
49 45
       if @user.save
@@ -106,6 +102,10 @@ class Admin::UsersController < ApplicationController
106 102
 
107 103
   private
108 104
 
105
+  def user_params
106
+    params.require(:user).permit(:email, :username, :password, :password_confirmation, :admin)
107
+  end
108
+
109 109
   def find_user
110 110
     @user = User.find(params[:id])
111 111
   end

+ 1 - 1
app/controllers/agents/dry_runs_controller.rb

@@ -14,7 +14,7 @@ module Agents
14 14
     end
15 15
 
16 16
     def create
17
-      attrs = params[:agent] || {}
17
+      attrs = agent_params
18 18
       if agent = current_user.agents.find_by(id: params[:agent_id])
19 19
         # POST /agents/:id/dry_run
20 20
         if attrs.present?

+ 3 - 3
app/controllers/agents_controller.rb

@@ -160,7 +160,7 @@ class AgentsController < ApplicationController
160 160
     @agent = current_user.agents.find(params[:id])
161 161
 
162 162
     respond_to do |format|
163
-      if @agent.update_attributes(params[:agent])
163
+      if @agent.update_attributes(agent_params)
164 164
         format.html { redirect_back "'#{@agent.name}' was successfully updated.", return: agents_path }
165 165
         format.json { render json: @agent, status: :ok, location: agent_path(@agent) }
166 166
       else
@@ -220,9 +220,9 @@ class AgentsController < ApplicationController
220 220
   end
221 221
 
222 222
   def build_agent
223
-    @agent = Agent.build_for_type(params[:agent].delete(:type),
223
+    @agent = Agent.build_for_type(agent_params[:type],
224 224
                                   current_user,
225
-                                  params[:agent])
225
+                                  agent_params.except(:type))
226 226
   end
227 227
 
228 228
   def initialize_presenter

+ 11 - 0
app/controllers/application_controller.rb

@@ -60,4 +60,15 @@ class ApplicationController < ActionController::Base
60 60
       @basecamp_agent = current_user.agents.where(type: 'Agents::BasecampAgent').first
61 61
     end
62 62
   end
63
+
64
+  def agent_params
65
+    return {} unless params[:agent]
66
+    @agent_params ||= begin
67
+      options = params[:agent].delete(:options) if params[:agent][:options].present?
68
+      params[:agent].permit(:memory, :name, :type, :schedule, :disabled, :keep_events_for, :propagate_immediately, :drop_pending_events,
69
+                            source_ids: [], receiver_ids: [], scenario_ids: [], controller_ids: [], control_target_ids: []).tap do |agent_params|
70
+        agent_params[:options] = options if options
71
+      end
72
+    end
73
+  end
63 74
 end

+ 9 - 2
app/controllers/scenarios_controller.rb

@@ -69,7 +69,7 @@ class ScenariosController < ApplicationController
69 69
   end
70 70
 
71 71
   def create
72
-    @scenario = current_user.scenarios.build(params[:scenario])
72
+    @scenario = current_user.scenarios.build(scenario_params)
73 73
 
74 74
     respond_to do |format|
75 75
       if @scenario.save
@@ -86,7 +86,7 @@ class ScenariosController < ApplicationController
86 86
     @scenario = current_user.scenarios.find(params[:id])
87 87
 
88 88
     respond_to do |format|
89
-      if @scenario.update_attributes(params[:scenario])
89
+      if @scenario.update_attributes(scenario_params)
90 90
         format.html { redirect_to @scenario, notice: 'This Scenario was successfully updated.' }
91 91
         format.json { head :no_content }
92 92
       else
@@ -115,4 +115,11 @@ class ScenariosController < ApplicationController
115 115
       format.json { head :no_content }
116 116
     end
117 117
   end
118
+
119
+  private
120
+
121
+  def scenario_params
122
+    params.require(:scenario).permit(:name, :description, :public, :source_url,
123
+                                     :tag_fg_color, :tag_bg_color, :icon, agent_ids: [])
124
+  end
118 125
 end

+ 8 - 2
app/controllers/user_credentials_controller.rb

@@ -48,7 +48,7 @@ class UserCredentialsController < ApplicationController
48 48
   end
49 49
 
50 50
   def create
51
-    @user_credential = current_user.user_credentials.build(params[:user_credential])
51
+    @user_credential = current_user.user_credentials.build(user_credential_params)
52 52
 
53 53
     respond_to do |format|
54 54
       if @user_credential.save
@@ -65,7 +65,7 @@ class UserCredentialsController < ApplicationController
65 65
     @user_credential = current_user.user_credentials.find(params[:id])
66 66
 
67 67
     respond_to do |format|
68
-      if @user_credential.update_attributes(params[:user_credential])
68
+      if @user_credential.update_attributes(user_credential_params)
69 69
         format.html { redirect_to user_credentials_path, notice: 'Your credential was successfully updated.' }
70 70
         format.json { head :no_content }
71 71
       else
@@ -84,4 +84,10 @@ class UserCredentialsController < ApplicationController
84 84
       format.json { head :no_content }
85 85
     end
86 86
   end
87
+
88
+  private
89
+
90
+  def user_credential_params
91
+    params.require(:user_credential).permit(:credential_name, :credential_value, :mode)
92
+  end
87 93
 end

+ 0 - 2
app/models/agent.rb

@@ -24,8 +24,6 @@ class Agent < ActiveRecord::Base
24 24
 
25 25
   EVENT_RETENTION_SCHEDULES = [["Forever", 0], ['1 hour', 1.hour], ['6 hours', 6.hours], ["1 day", 1.day], *([2, 3, 4, 5, 7, 14, 21, 30, 45, 90, 180, 365].map {|n| ["#{n} days", n.days] })]
26 26
 
27
-  attr_accessible :options, :memory, :name, :type, :schedule, :controller_ids, :control_target_ids, :disabled, :source_ids, :receiver_ids, :scenario_ids, :keep_events_for, :propagate_immediately, :drop_pending_events
28
-
29 27
   json_serialize :options, :memory
30 28
 
31 29
   validates_presence_of :name, :user

+ 0 - 2
app/models/agent_log.rb

@@ -2,8 +2,6 @@
2 2
 # in Agents' detail pages.  AgentLogs with a `level` of 4 or greater are considered "errors" and automatically update
3 3
 # Agents' `last_error_log_at` column.  These are often used to determine if an Agent is `working?`.
4 4
 class AgentLog < ActiveRecord::Base
5
-  attr_accessible :agent, :inbound_event, :level, :message, :outbound_event
6
-
7 5
   belongs_to :agent
8 6
   belongs_to :inbound_event, :class_name => "Event"
9 7
   belongs_to :outbound_event, :class_name => "Event"

+ 0 - 2
app/models/control_link.rb

@@ -1,7 +1,5 @@
1 1
 # A ControlLink connects Agents in a control flow from the `controller` to the `control_target`.
2 2
 class ControlLink < ActiveRecord::Base
3
-  attr_accessible :controller_id, :target_id
4
-
5 3
   belongs_to :controller, class_name: 'Agent', inverse_of: :control_links_as_controller
6 4
   belongs_to :control_target, class_name: 'Agent', inverse_of: :control_links_as_control_target
7 5
 end

+ 0 - 2
app/models/event.rb

@@ -7,8 +7,6 @@ class Event < ActiveRecord::Base
7 7
   include JSONSerializedField
8 8
   include LiquidDroppable
9 9
 
10
-  attr_accessible :lat, :lng, :location, :payload, :user_id, :user, :expires_at
11
-
12 10
   acts_as_mappable
13 11
 
14 12
   json_serialize :payload

+ 0 - 2
app/models/link.rb

@@ -1,7 +1,5 @@
1 1
 # A Link connects Agents in a directed Event flow from the `source` to the `receiver`.
2 2
 class Link < ActiveRecord::Base
3
-  attr_accessible :source_id, :receiver_id
4
-
5 3
   belongs_to :source, :class_name => "Agent", :inverse_of => :links_as_source
6 4
   belongs_to :receiver, :class_name => "Agent", :inverse_of => :links_as_receiver
7 5
 

+ 0 - 3
app/models/scenario.rb

@@ -1,9 +1,6 @@
1 1
 class Scenario < ActiveRecord::Base
2 2
   include HasGuid
3 3
 
4
-  attr_accessible :name, :agent_ids, :description, :public, :source_url,
5
-                  :tag_fg_color, :tag_bg_color, :icon
6
-
7 4
   belongs_to :user, :counter_cache => :scenario_count, :inverse_of => :scenarios
8 5
   has_many :scenario_memberships, :dependent => :destroy, :inverse_of => :scenario
9 6
   has_many :agents, :through => :scenario_memberships, :inverse_of => :scenarios

+ 0 - 2
app/models/service.rb

@@ -1,6 +1,4 @@
1 1
 class Service < ActiveRecord::Base
2
-  attr_accessible :provider, :name, :token, :secret, :refresh_token, :expires_at, :global, :options, :uid
3
-
4 2
   serialize :options, Hash
5 3
 
6 4
   belongs_to :user, :inverse_of => :services

+ 0 - 5
app/models/user.rb

@@ -12,11 +12,6 @@ class User < ActiveRecord::Base
12 12
   # This is in addition to a real persisted field like 'username'
13 13
   attr_accessor :login
14 14
 
15
-  ACCESSIBLE_ATTRIBUTES = [ :email, :username, :login, :password, :password_confirmation, :remember_me, :invitation_code ]
16
-
17
-  attr_accessible *ACCESSIBLE_ATTRIBUTES
18
-  attr_accessible *(ACCESSIBLE_ATTRIBUTES + [:admin]), :as => :admin
19
-
20 15
   validates_presence_of :username
21 16
   validates :username, uniqueness: { case_sensitive: false }
22 17
   validates_format_of :username, :with => /\A[a-zA-Z0-9_-]{3,15}\Z/, :message => "can only contain letters, numbers, underscores, and dashes, and must be between 3 and 15 characters in length."

+ 0 - 2
app/models/user_credential.rb

@@ -1,8 +1,6 @@
1 1
 class UserCredential < ActiveRecord::Base
2 2
   MODES = %w[text java_script]
3 3
 
4
-  attr_accessible :credential_name, :credential_value, :mode
5
-
6 4
   belongs_to :user
7 5
 
8 6
   validates_presence_of :credential_name

+ 0 - 6
config/application.rb

@@ -39,12 +39,6 @@ module Huginn
39 39
     # like if you have constraints or database-specific column types
40 40
     # config.active_record.schema_format = :sql
41 41
 
42
-    # Enforce whitelist mode for mass assignment.
43
-    # This will create an empty whitelist of attributes available for mass-assignment for all models
44
-    # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
45
-    # parameters by using an attr_accessible or attr_protected declaration.
46
-    config.active_record.whitelist_attributes = true
47
-
48 42
     # Do not swallow errors in after_commit/after_rollback callbacks.
49 43
     config.active_record.raise_in_transactional_callbacks = true
50 44
 

+ 2 - 2
config/environments/development.rb

@@ -26,8 +26,8 @@ Huginn::Application.configure do
26 26
   # Only use best-standards-support built into browsers
27 27
   config.action_dispatch.best_standards_support = :builtin
28 28
 
29
-  # Raise exception on mass assignment protection for Active Record models
30
-  config.active_record.mass_assignment_sanitizer = :strict
29
+  # Raise exception for unpermitted parameters
30
+  config.action_controller.action_on_unpermitted_parameters = :raise
31 31
 
32 32
   # Raise an error on page load if there are pending migrations.
33 33
   config.active_record.migration_error = :page_load

+ 2 - 2
config/environments/test.rb

@@ -33,8 +33,8 @@ Huginn::Application.configure do
33 33
 
34 34
   config.action_mailer.raise_delivery_errors = true
35 35
 
36
-  # Raise exception on mass assignment protection for Active Record models
37
-  config.active_record.mass_assignment_sanitizer = :strict
36
+  # Raise exception for unpermitted parameters
37
+  config.action_controller.action_on_unpermitted_parameters = :raise
38 38
 
39 39
   # Randomize the order test cases are executed.
40 40
   config.active_support.test_order = :random

+ 1 - 1
spec/controllers/admin/users_controller_spec.rb

@@ -15,7 +15,7 @@ describe Admin::UsersController do
15 15
       it 'does not import the default scenario' do
16 16
         stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
17 17
         sign_in users(:jane)
18
-        post :create, :user => {}
18
+        post :create, :user => {username: 'user'}
19 19
       end
20 20
     end
21 21
   end

+ 7 - 0
spec/controllers/agents_controller_spec.rb

@@ -280,6 +280,13 @@ describe AgentsController do
280 280
       expect(response).to render_template("edit")
281 281
     end
282 282
 
283
+    it 'does not allow to modify the agents user_id' do
284
+      sign_in users(:bob)
285
+      expect {
286
+        post :update, :id => agents(:bob_website_agent).to_param, :agent => valid_attributes(:user_id => users(:jane).id)
287
+      }.to raise_error(ActionController::UnpermittedParameters)
288
+    end
289
+
283 290
     describe "redirecting back" do
284 291
       before do
285 292
         sign_in users(:bob)

+ 7 - 1
spec/controllers/scenarios_controller_spec.rb

@@ -116,7 +116,7 @@ describe ScenariosController do
116 116
     it "will not create Scenarios for other users" do
117 117
       expect {
118 118
         post :create, :scenario => valid_attributes(:user_id => users(:jane).id)
119
-      }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
119
+      }.to raise_error(ActionController::UnpermittedParameters)
120 120
     end
121 121
   end
122 122
 
@@ -138,6 +138,12 @@ describe ScenariosController do
138 138
       expect(assigns(:scenario)).to have(1).errors_on(:name)
139 139
       expect(response).to render_template("edit")
140 140
     end
141
+
142
+    it 'adds an agent to the scenario' do
143
+      expect {
144
+        post :update, :id => scenarios(:bob_weather).to_param, :scenario => { :name => "new_name", :public => "1", agent_ids: scenarios(:bob_weather).agent_ids + [agents(:bob_website_agent).id] }
145
+      }.to change { scenarios(:bob_weather).agent_ids.length }.by(1)
146
+    end
141 147
   end
142 148
 
143 149
   describe 'PUT enable_or_disable_all_agents' do

+ 1 - 1
spec/controllers/user_credentials_controller_spec.rb

@@ -69,7 +69,7 @@ describe UserCredentialsController do
69 69
     it "will not create UserCredentials for other users" do
70 70
       expect {
71 71
         post :create, :user_credential => valid_attributes(:user_id => users(:jane).id)
72
-      }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
72
+      }.to raise_error(ActionController::UnpermittedParameters)
73 73
     end
74 74
   end
75 75
 

+ 9 - 3
spec/controllers/users/registrations_controller_spec.rb

@@ -5,13 +5,16 @@ module Users
5 5
     include Devise::TestHelpers
6 6
 
7 7
     describe "POST create" do
8
+      before do
9
+        @request.env["devise.mapping"] = Devise.mappings[:user]
10
+      end
11
+
8 12
       context 'with valid params' do
9 13
         it "imports the default scenario for the new user" do
10 14
           mock(DefaultScenarioImporter).import(is_a(User))
11 15
 
12
-          @request.env["devise.mapping"] = Devise.mappings[:user]
13 16
           post :create, :user => {username: 'jdoe', email: 'jdoe@example.com',
14
-            password: 's3cr3t55', password_confirmation: 's3cr3t55', admin: false, invitation_code: 'try-huginn'}
17
+            password: 's3cr3t55', password_confirmation: 's3cr3t55', invitation_code: 'try-huginn'}
15 18
         end
16 19
       end
17 20
 
@@ -19,10 +22,13 @@ module Users
19 22
         it "does not import the default scenario" do
20 23
           stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
21 24
 
22
-          @request.env["devise.mapping"] = Devise.mappings[:user]
23 25
           setup_controller_for_warden
24 26
           post :create, :user => {}
25 27
         end
28
+
29
+        it 'does not allow to set the admin flag' do
30
+          expect { post :create, :user => {admin: 'true'} }.to raise_error(ActionController::UnpermittedParameters)
31
+        end
26 32
       end
27 33
     end
28 34
   end

+ 0 - 8
spec/models/user_credential_spec.rb

@@ -8,14 +8,6 @@ describe UserCredential do
8 8
     it { should validate_presence_of(:user_id) }
9 9
   end
10 10
 
11
-  describe "mass assignment" do
12
-    it { should allow_mass_assignment_of :credential_name }
13
-
14
-    it { should allow_mass_assignment_of :credential_value }
15
-
16
-    it { should_not allow_mass_assignment_of :user_id }
17
-  end
18
-
19 11
   describe "cleaning fields" do
20 12
     it "should trim whitespace" do
21 13
       user_credential = user_credentials(:bob_aws_key)